package com.tiancheng.trade.authserver.service.impl;

import com.tiancheng.trade.authserver.component.login.UserLoginHandlerFactory;
import com.tiancheng.trade.authserver.constant.Oauth2Scop;
import com.tiancheng.trade.authserver.constant.SystemConstant;
import com.tiancheng.trade.authserver.dto.*;
import com.tiancheng.trade.authserver.entity.AuthApplication;
import com.tiancheng.trade.authserver.enums.LogInTypeEnum;
import com.tiancheng.trade.authserver.enums.ThirdPlatformEnums;
import com.tiancheng.trade.authserver.service.IAuthApplicationService;
import com.tiancheng.trade.authserver.service.IAuthOauth2Service;
import com.tiancheng.trade.authserver.utils.AuthCacheUtil;
import com.tiancheng.trade.authserver.vo.UserLogInResultVO;
import com.tiancheng.trade.authserver.vo.UserLoginInVO;
import com.tiancheng.trade.commom.web.config.PlatformRequestConfig;
import com.tiancheng.trade.commom.core.exception.AuthorizationException;
import com.tiancheng.trade.commom.core.exception.error.AuthErrorInfoEnum;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

@Slf4j
@Service
public class IAuthOauth2ServiceImpl implements IAuthOauth2Service {
    @Resource
    private IAuthApplicationService applicationService;
    @Resource
    private UserLoginHandlerFactory userLoginHandlerFactory;
    @Resource
    private PlatformRequestConfig platformRequestConfig;

    /**
     * 获取oauth2 授权码
     *
     * @param applicationInfo 应用程序信息
     * @param userInfo        用户登陆信息
     * @return 返回 oauth2 授权码
     */
    @Override
    public Oauth2AuthorizationResult oauth2Authorization(Oauth2AuthorizationRequestDTO applicationInfo, UserLoginInVO userInfo,
                                                         boolean checkCertificate) {
        // 参数校验
        AuthApplication application = this.applicationService.getByTypeAndClientId(ThirdPlatformEnums.tiancheng,
                applicationInfo.getClient_id());
        AuthErrorInfoEnum.AUTH_NO_SIGN_IN_ERROR_CODE.assertNotNull(application);
        userInfo.setClientId(application.getClientId());
        UserLogInResultVO signInResult;
        if (StringUtils.isNotEmpty(userInfo.getPassword())) {
            signInResult = userLoginHandlerFactory.getHandler(LogInTypeEnum.Oauth2Password).signIn(userInfo, checkCertificate);
        } else {
            signInResult = userLoginHandlerFactory.getHandler(LogInTypeEnum.Oauth2Code).signIn(userInfo, checkCertificate);
        }
        String authCode = UUID.randomUUID().toString().replaceAll("-", "").substring(8, 24);
        HashMap<String, String> authCodeInfo = new HashMap<>();
        authCodeInfo.put("clientId", applicationInfo.getClient_id());
        authCodeInfo.put("applicationId", application.getId().toString());
        authCodeInfo.put("userId", signInResult.getId().toString());
        authCodeInfo.put("scope", applicationInfo.getScope());
        authCodeInfo.put("phone", userInfo.getPhone());
        authCodeInfo.put("email", userInfo.getEmail());
        authCodeInfo.put("signInResult", JsonUtil.toJsonString(signInResult));
        AuthCacheUtil.cacheOauth2AuthCode(authCode , authCodeInfo);
        log.info("oauth2登录, 用户id:{}, 手机号:{}, oauth2Code:{}", signInResult.getId(), signInResult.getPhone(),
                authCode);
        List<CookieDTO> cookies = new LinkedList<>();
        cookies.add(new CookieDTO().setName("jwt").setValue(signInResult.getJwt())
                .setDomain(this.platformRequestConfig.getRootDomain()).setMaxAge((int) SystemConstant.userTokenTimeout)
                .setPath("/").setHttpOnly(false));
        cookies.add(new CookieDTO().setName("w_skey").setValue(signInResult.getJwt())
                .setDomain(this.platformRequestConfig.getRootDomain()).setMaxAge((int) SystemConstant.userTokenTimeout)
                .setPath("/").setHttpOnly(false));
        cookies.add(new CookieDTO().setName("w_uid").setValue(signInResult.getId().toString())
                .setDomain(this.platformRequestConfig.getRootDomain()).setMaxAge((int) SystemConstant.userTokenTimeout)
                .setPath("/").setHttpOnly(false));
        cookies.add(new CookieDTO().setName("w_open_id").setValue(signInResult.getOpenId())
                .setDomain(this.platformRequestConfig.getRootDomain()).setMaxAge((int) SystemConstant.userTokenTimeout)
                .setPath("/").setHttpOnly(false));
        cookies.add(new CookieDTO().setName("w_expires_in").setValue(SystemConstant.userTokenTimeout + "")
                .setDomain(this.platformRequestConfig.getRootDomain()).setMaxAge((int) SystemConstant.userTokenTimeout)
                .setPath("/").setHttpOnly(false));
        return new Oauth2AuthorizationResult().setOauth2Code(authCode).setCookies(cookies);
    }

    /**
     * oauth2授权码换取access token
     */
    @Override
    public AccessTokenResponseDTO getOauthAccessToken(AccessTokenRequestDTO param) {
        log.info("oauth2授权码换取accessToken,code值:{}", param.getCode());
        AuthApplication application = this.applicationService.getByTypeAndClientId(ThirdPlatformEnums.tiancheng,
                param.getClient_id());

        AuthErrorInfoEnum.AUTH_APPLICATION_APPLICATION_NOT_FOUND.assertNotNull(application);
        if (!param.getClient_secret().equals(application.getClientSecret())) {
            throw new AuthorizationException(AuthErrorInfoEnum.AUTH_ERROR);
        }
        HashMap<String, String> authCode = new HashMap<>();
        AuthCacheUtil.getOauth2AuthCode(param.getCode()).ifPresent(authCode::putAll);
        if (authCode.isEmpty()) {
            throw new AuthorizationException( "invalid code");
        }
        if (!param.getClient_id().equals(authCode.get("clientId"))) {
            throw new AuthorizationException( "authorization code and signature do not match");
        }
        String accessToken = UUID.randomUUID().toString().replaceAll("-", "");
        AuthCacheUtil.cacheOauth2AccessToken(accessToken, authCode);
        AccessTokenResponseDTO result = new AccessTokenResponseDTO();
        result.setAccess_token(accessToken);
        result.setExpires_in(SystemConstant.oauth2AccessTokenTimeout);
        result.setToken_type("bearer");
        result.setScope(param.getScope());
        result.setRefresh_token(null);
        AuthCacheUtil.delOauth2AuthCode(param.getCode());
        log.info("oauth2换取accessToken,code值:{},值:{}", param.getCode(), result.getAccess_token());
        return result;
    }

    /**
     * 使用oauth2 access token换取用户信息
     */
    @Override
    public UserLogInResultVO oauth2UserInfo(String authorization) {
        log.info("oauth2获取用户信息参数:{}", authorization);
        authorization = authorization.substring(authorization.indexOf(" ") + 1);
        HashMap<String, String> authInfo = new HashMap<>();
        AuthCacheUtil.getOauth2AccessToken(authorization).ifPresent(authInfo::putAll);
        if (authInfo.isEmpty()) {
            throw new AuthorizationException(401, "invalid access token");
        }
        String phone = authInfo.get("phone");
        String email = authInfo.get("email");
        String scope = authInfo.get("scope");
        UserLogInResultVO result = JsonUtil.fromJson(authInfo.get("signInResult"), UserLogInResultVO.class);
        result.setEmail(null);
        result.setPhone(null);
        Set<String> permissions = result.getPermissions();
        Set<String> roles = result.getRoles();
        result.setPermissions(null);
        result.setRoles(null);
        if (Oauth2Scop.userAuth.equals(scope)) {
            result.setPermissions(permissions);
            result.setRoles(roles);
            result.setPhone(phone);
            result.setEmail(email);
        }
        if (Oauth2Scop.userInfo.equals(scope)) {
            result.setPhone(phone);
            result.setEmail(email);
        }
        AuthCacheUtil.delOauth2AccessToken(authorization);
        log.info("获取oauth2用户信息结果:{}", JsonUtil.toJsonString(result));
        return result;
    }

    @Override
    public UserLogInResultVO oauth2UserInfoByCode(String code) {
        log.info("oauth2获取用户信息参数:{}", code);
        HashMap<String, String> authInfo = new HashMap<>();
        AuthCacheUtil.getOauth2AuthCode(code).ifPresent(authInfo::putAll);
        if (authInfo.isEmpty()) {
            throw new AuthorizationException(401, "invalid access token");
        }
        String phone = authInfo.get("phone");
        String email = authInfo.get("email");
        String scope = authInfo.get("scope");
        UserLogInResultVO result = JsonUtil.fromJson(authInfo.get("signInResult"), UserLogInResultVO.class);
        result.setEmail(null);
        result.setPhone(null);
        Set<String> permissions = result.getPermissions();
        Set<String> roles = result.getRoles();
        result.setPermissions(null);
        result.setRoles(null);
        if (Oauth2Scop.userAuth.equals(scope)) {
            result.setPermissions(permissions);
            result.setRoles(roles);
            result.setPhone(phone);
            result.setEmail(email);
        }
        if (Oauth2Scop.userInfo.equals(scope)) {
            result.setPhone(phone);
            result.setEmail(email);
        }
        //AuthCacheUtil.delOauth2AccessToken(authorization);
        log.info("获取oauth2用户信息结果:{}", JsonUtil.toJsonString(result));
        return result;
    }

}
